
#include <BWidget>

#include <BGridLayout>
#include "member_BGridLayout.h"
#include "member_BWidget.h"
#include "member_BSpacer.h"
#include "member_BSpring.h"

using namespace BWE;

#define member					(*(member_BGridLayout*)_ptr)
#define member_allocate()		_ptr = new member_BGridLayout(this)
#define member_release()		delete (member_BGridLayout*)_ptr

BGridLayout::BGridLayout(BWidget* host) : BLayout(host)
{
	member_allocate();
	this->setSizePolicy(BSizePolicy(Policy_Dynamic, Policy_Dynamic));
}
BGridLayout::BGridLayout(const BString& name, BWidget* host) : BLayout(name, host)
{
	member_allocate();
	this->setSizePolicy(BSizePolicy(Policy_Dynamic, Policy_Dynamic));
}
BGridLayout::BGridLayout(Policy widthPolicy, Policy heightPolicy, BWidget* host) : BLayout(host)
{
	member_allocate();
	this->setSizePolicy(BSizePolicy(widthPolicy, heightPolicy));
}
BGridLayout::~BGridLayout()
{
	member_release();
}

bool BGridLayout::setWidget(int row, int column, BWidget* widget, Align align)
{
	if (!widget)
		return false;

	if (this->host() == widget)
		return false;

	if (widget_member(widget)->layout == this)
	{
		auto it = member.items.find(widget);
		if (it.row() != row || it.column() != column)
		{
			LayoutItem& item = member.items(row, column);
			item = widget;
			it->clear();
		}
		return true;
	}

	member.expandTable(row, column);

	LayoutItem& item = member.items[row][column];
	item.detach(this->host());
	item.widget = widget;
	item.align = align;
	if (widget_member(widget)->layout)
		widget_member(widget)->layout->remove(widget);
	widget_member(widget)->layout = this;
	item.attach(this->host());
	this->dirty();
	return true;
}
bool BGridLayout::setLayout(int row, int column, BLayout* layout, Align align)
{
	if (!layout || layout == this)
		return false;

	if (layout_member(layout)->parent == this)
	{
		auto it = member.items.find(layout);
		if (it.row() != row || it.column() != column)
		{
			LayoutItem& item = member.items(row, column);
			item = layout;
			it->clear();
		}
		return true;
	}

	member.expandTable(row, column);

	LayoutItem& item = member.items[row][column];
	item = layout;
	item.align = align;
	item.attach(this->host());

	if (layout_member(layout)->parent)
		layout_member(layout)->parent->remove(layout);
	layout_member(layout)->parent = this;

	this->dirty();
	return true;
}
bool BGridLayout::setSpacer(int row, int column, BSpacer* spacer, Align align)
{
	if (!spacer)
		return false;

	if (spacer_member(spacer)->layout == this)
	{
		auto it = member.items.find(spacer);
		if (it.row() != row || it.column() != column)
		{
			LayoutItem& item = member.items(row, column);
			item = spacer;
			it->clear();
		}
		return true;
	}

	member.expandTable(row, column);

	LayoutItem& item = member.items[row][column];
	item = spacer;
	item.align = align;

	if (spacer_member(spacer)->layout)
		spacer_member(spacer)->layout->remove(spacer);
	spacer_member(spacer)->layout = this;

	this->dirty();
	return true;
}
bool BGridLayout::setSpring(int row, int column, BSpring* spring)
{
	if (!spring)
		return false;

	if (spring_member(spring)->layout == this)
	{
		auto it = member.items.find(spring);
		if (it.row() != row || it.column() != column)
		{
			LayoutItem& item = member.items(row, column);
			item = spring;
			it->clear();
		}
		return true;
	}

	member.expandTable(row, column);

	LayoutItem& item = member.items(row, column);
	item.detach(this->host());
	item = spring;

	if (spring_member(spring)->layout)
		spring_member(spring)->layout->remove(spring);
	spring_member(spring)->layout = this;

	this->dirty();
	return true;
}

int BGridLayout::rowCount() const
{
	return member.items.row();
}

bool BGridLayout::setColumnCount(int columnCount)
{
	member.expandTable(member.rows.size(), columnCount - 1);
	return true;
}
int BGridLayout::columnCount() const
{
	return member.items.column();
}

bool BGridLayout::remove(int row, int column)
{
	if (!member.items.check(row, column))
		return false;
	LayoutItem& item = member.items(row, column);
	if (item.empty())
		return false;
	item.widget = 0;
	item.spring = 0;
	item.layout = 0;
	this->dirty();
	return true;
}
BWidget* BGridLayout::widget(int row, int column) const
{
	if (member.items.check(row, column))
		return 0;
	LayoutItem& item = member.items[row][column];
	return item.widget;
}
BLayout* BGridLayout::layout(int row, int column) const
{
	if (member.items.check(row, column))
		return 0;
	LayoutItem& item = member.items[row][column];
	return item.layout;
}
BSpacer* BGridLayout::spacer(int row, int column) const
{
	if (member.items.check(row, column))
		return 0;
	LayoutItem& item = member.items[row][column];
	return item.spacer;
}
BSpring* BGridLayout::spring(int row, int column) const
{
	if (member.items.check(row, column))
		return 0;
	LayoutItem& item = member.items[row][column];
	return item.spring;
}

void BGridLayout::setColumnStretch(int column, float stretch)
{
	if (!member.cols.check(column))
		return;
	if (member.cols[column].fixedStretch != stretch)
	{
		member.cols[column].fixedStretch = stretch;
		this->dirty();
	}
}
float BGridLayout::columnStretch(int column) const
{
	return member.cols(column).fixedStretch;
}

void BGridLayout::setRowStretch(int row, float stretch)
{
	if (!member.rows.check(row))
		return;
	if (member.rows[row].fixedStretch != stretch)
	{
		member.rows[row].fixedStretch = stretch;
		this->dirty();
	}
}
float BGridLayout::rowStretch(int row) const
{
	return member.rows(row).fixedStretch;
}

BRect BGridLayout::cellRect(int row, int column)
{
	return member.items(row, column).rect;
}

bool BGridLayout::setAlign(int row, int column, Align align)
{
	if (!member.items.check(row, column))
		return false;
	LayoutItem& item = member.items[row][column];
	if (item.align != align)
	{
		item.align = align;
		this->dirty();
	}
	return true;
}
Align BGridLayout::align(int row, int column) const
{
	if (!member.items.check(row, column))
		return Align_None;
	LayoutItem& item = member.items[row][column];
	return item.align;
}

void BGridLayout::setHost(BWidget* host)
{
	if (!host)
		return;
	if (this->contain(host))
		return;
	if (this->host() == host)
		return;
	if (BWidget* widget = this->host())
	{
		for (LayoutItem& item : member.items)
		{
			item.detach(widget);
		}
	}
	BLayout::setHost(host);
	if (BWidget* widget = this->host())
	{
		for (LayoutItem& item : member.items)
		{
			item.attach(widget);
		}
	}
}
bool BGridLayout::contain(const BWidget* widget) const
{
	if (!widget)
		return false;
	for (int r = 0; r < member.items.row(); r++)
	{
		for (int c = 0; c < member.items.column(); c++)
		{
			const LayoutItem& item = member.items[r][c];
			if (item.widget == widget)
				return true;
			if (item.layout && item.layout->contain(widget))
				return true;
		}
	}
	return false;
}
bool BGridLayout::contain(const BLayout* layout) const
{
	return false;
}
bool BGridLayout::contain(const BSpacer* spacer) const
{
	return false;
}
bool BGridLayout::contain(const BSpring* spring) const
{
	return false;
}

bool BGridLayout::remove(const BWidget* widget)
{
	if (!widget)
		return false;
	if (widget_member(widget)->layout == this)
	{
		widget_member(widget)->layout = 0;
		member.items.remove(widget);
		this->dirty();
		return true;
	}
	return false;
}
bool BGridLayout::remove(const BLayout* layout)
{
	if (!layout)
		return false;
	for (LayoutItem& item : member.items)
	{
		if (item.layout == layout)
		{
			item.layout = 0;
			this->dirty();
			return true;
		}
	}
	return false;
}
bool BGridLayout::remove(const BSpacer* spacer)
{
	if (!spacer)
		return false;
	for (LayoutItem& item : member.items)
	{
		if (item.spacer == spacer)
		{
			item.spacer = 0;
			this->dirty();
			return true;
		}
	}
	return false;
}
bool BGridLayout::remove(const BSpring* spring)
{
	if (!spring)
		return false;
	for (LayoutItem& item : member.items)
	{
		if (item.spring == spring)
		{
			item.spring = 0;
			this->dirty();
			return true;
		}
	}
	return false;
}

void BGridLayout::clear()
{
	if(member.items.size())
	{
		member.items.clear();
	}
}

BSizeStretch BGridLayout::sizeStretch() const
{
	BSizeStretch stretch = BLayout::sizeStretch();
	if (stretch == BSizeStretch())
	{
		for (int i = 0; i < member.cols.size(); i++)
			stretch.widthStretch() += member.cols[i].stretch;
		for (int r = 0; r < member.rows.size(); r++)
			stretch.heightStretch() += member.rows[r].stretch;
	}
	return stretch;
}
BSizePolicy BGridLayout::sizePolicy() const
{
	return layout_member(this)->sizePolicy;
	BSizePolicy policy = layout_member(this)->sizePolicy;
	if (policy.widthPolicy() == Policy_None || policy.heightPolicy() == Policy_None)
	{
		int border = this->border();
		int spacing = this->spacing();
		member.prepareCells();
		if (policy.widthPolicy() == Policy_None)
		{
			for (int i = 0; i < member.cols.size(); i++)
				policy.widthPolicy() |= member.cols[i].policy;
		}
		if (policy.heightPolicy() == Policy_None)
		{
			for (int i = 0; i < member.rows.size(); i++)
				policy.heightPolicy() |= member.rows[i].policy;
		}
	}
	return policy;

}
BSize BGridLayout::fixedSize() const
{
	BSize fixedSize = layout_member(this)->fixedSize;
	if (fixedSize.width() == 0 || fixedSize.height() == 0)
	{
		int border = this->border();
		int spacing = this->spacing();
		int fixedWidth = fixedSize.width();
		int fixedHeight = fixedSize.height();
		member.prepareCells();
		for (int i = 0; i < member.cols.size(); i++)
			fixedWidth += spacing + member.cols[i].fixedSize.width();
		for (int i = 0; i < member.rows.size(); i++)
			fixedHeight += spacing + member.rows[i].fixedSize.height();
		if (fixedSize.width() == 0)
			fixedSize.width() = fixedWidth;
		if (fixedSize.height() == 0)
			fixedSize.height() = fixedHeight;
	}
	return fixedSize;
}
BSize BGridLayout::sizeHint() const
{
	member.prepareCells();
	int border = this->border();
	int spacing = this->spacing();
	int hintWidth = border + border;
	int hintHeight = border + border;

	for (int i = 0; i < member.cols.size(); i++)
		if (member.cols[i].policy == Policy_Fixed)
			hintWidth += spacing + member.cols[i].fixedSize.width();
		else
			hintWidth += spacing + member.cols[i].sizeHint.width();

	for(int i = 0; i < member.rows.size(); i++)
		if (member.rows[i].policy == Policy_Fixed)
			hintHeight += spacing + member.rows[i].fixedSize.height();
		else
			hintHeight += spacing + member.rows[i].sizeHint.height();

	BSize hsize(hintWidth - spacing, hintHeight - spacing);
	if (hsize.width() < 0)
		hsize.width() = 0;
	if (hsize.height() < 0)
		hsize.height() = 0;
	return hsize;
}
BSize BGridLayout::minSize() const
{
	if (member.items.empty())
	{
		return layout_member(this)->minSize;
	}
	int border = this->border();
	int spacing = this->spacing();
	int minWidth = border + border;
	int minHeight = border + border;
	for (int i = 0; i < member.cols.size(); i++)
		minWidth += spacing + member.cols[i].minSize.width();
	for (int i = 0; i < member.rows.size(); i++)
		minHeight += spacing + member.rows[i].minSize.height();
	minWidth -= spacing;
	minHeight -= spacing;
	BSize minSize = layout_member(this)->minSize;
	if (minSize.width() < minWidth)
		minSize.width() = minWidth;
	if (minSize.height() < minHeight)
		minSize.height() = minHeight;
	return minSize;

}
BSize BGridLayout::size() const
{
	BSize size;
	if (member.items.size())
	{
		for (int i = 0; i < member.cols.size(); i++)
			size.width() += member.cols[i].width;
		for (int r = 0; r < member.rows.size(); r++)
			size.height() += member.rows[r].height;
	}
	return size;
}
bool BGridLayout::adjust() const
{
	if (!this->isdirty())
		return false;

	if (this->rect().invalid())
	{
		for (auto it = member.items.begin(); it != member.items.end(); it++)
		{
			it->adjust(0, 0, 0, 0);
		}
		this->clean();
		return false;
	}

	const BRect& rect = this->rect();
	int x = rect.x();
	int y = rect.y();
	int w = rect.width();
	int h = rect.height();

	if (this->name() == "debug")
		int a = 0;
	BSize hintSize = this->sizeHint();

	//for width
	int remainWidth = w - hintSize.width();
	member.distributeWidth(remainWidth);

	//for height
	int remainHeight = h - hintSize.height();
	member.distributeHeight(remainHeight);

	//for result
	int border = this->border();
	int spacing = this->spacing();
	int posY = y + border;
	for (int r = 0; r < member.items.row(); r++)
	{
		int posX = x + border;
		for (int c = 0; c < member.items.column(); c++)
		{
			if (member.cols[c].ignored && member.rows[r].ignored)
				continue;
			int width = member.cols[c].width;
			int height = member.rows[r].height;
			LayoutItem& item = member.items[r][c];
			item.adjust(posX, posY, width, height);
			posX += spacing + width;
		}
		posY += spacing + member.rows[r].height;
	}

	this->clean();
	return true;
}

